// RedJey Graphics User Interface Library for J2ME Platform.
// Copyright (C) 2008 Yevgen Krapiva
//
// This library is free software; you can redistribute
// it and/or modify it under the terms of the
// GNU Lesser General Public License as published
// by the Free Software Foundation; either version 3.0
// of the License, or any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

package com.incrediblesoft.redjey.font4me;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.IndexColorModel;
import java.awt.image.BufferedImage;
import java.util.Vector;
import java.io.DataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * @author Yevgen Krapiva<br>
 *         Homepage: http://code.google.com/p/redjey/<br>
 *         Feedback: ykrapiva@gmail.com<br>
 *         Copyright (C) 2008 Yevgen Krapiva<br>
 */

public class CustomFont
{
    private BufferedImage fontImage;
	byte[] characterWidths;
	short[] xPositions;
	private String characterMap;
	private int fontHeight;
    private boolean isCaseSensitive;
    private Color foregroundColor, backgroundColor;

    public CustomFont(Graphics gr, Font font, String charmap, Color foregroundColor, Color backgroundColor, boolean caseSensitive)
    {
        if (gr != null && font != null && charmap != null && charmap.length() > 0)
        {
            this.characterMap = charmap;
            this.foregroundColor = foregroundColor;
            this.backgroundColor = backgroundColor;
            this.isCaseSensitive = caseSensitive;

            boolean transparentBackground = backgroundColor == null;

            FontMetrics fm = gr.getFontMetrics(font);

            int imageWidth = fm.stringWidth(charmap);
            int imageHeight = fm.getAscent() + fm.getDescent();
            fontHeight = imageHeight;
            //System.out.println("asc: "+fm.getAscent()+"  height: "+fm.getHeight()+"  maxAscent: "+fm.getMaxAscent()+ "descent: "+fm.getDescent()+"  maxDescent: "+fm.getMaxDescent());

            byte[] r = {transparentBackground ? 1 : (byte) backgroundColor.getRed(), (byte) foregroundColor.getRed()};
            byte[] g = {transparentBackground ? 1 : (byte) backgroundColor.getGreen(), (byte) foregroundColor.getGreen()};
            byte[] b = {transparentBackground ? 1 : (byte) backgroundColor.getBlue(), (byte) foregroundColor.getBlue()};
            byte[] a = {transparentBackground ? 0 : (byte) backgroundColor.getAlpha(), (byte) foregroundColor.getAlpha()};

            IndexColorModel icm = new IndexColorModel(8, r.length, r, g, b, a);

            fontImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_BYTE_INDEXED, icm);
            Graphics2D imageGraphics = fontImage.createGraphics();
            imageGraphics.setClip(0, 0, imageWidth, imageHeight);
            imageGraphics.setFont(font);
            imageGraphics.setBackground(backgroundColor);
            imageGraphics.setColor(foregroundColor);

            //imageGraphics.drawString(charmap, 0, fm.getAscent() );
            short xPos = 0;
            char[] chars = new char[1];

            characterWidths = new byte[charmap.length()];
            xPositions = new short[charmap.length()];

            for (int i=0; i<charmap.length(); i++)
            {
                char ch = charmap.charAt(i);
                byte chWidth = (byte) fm.charWidth(ch);
                characterWidths[i] = (byte) (chWidth);

                Rectangle clipRect = imageGraphics.getClipBounds();
                imageGraphics.clipRect(xPos, 0, characterWidths[i], imageHeight);
                chars[0] = ch;
                imageGraphics.drawChars(chars, 0, 1, xPos, fm.getAscent());
                imageGraphics.setClip(clipRect.x, clipRect.y, clipRect.width, clipRect.height);

                xPositions[i] = xPos;                
                xPos += characterWidths[i];
            }
        }
    }    

    public int getHeight()
    {
        return fontHeight;
    }

    public int charWidth(char ch)
    {
        int index = characterMap.indexOf(isCaseSensitive ? ch : Character.toLowerCase(ch));
        if (index == -1) index = characterMap.indexOf('?');
        if (index != -1)
            return this.characterWidths[index];
        return -1;
    }

    public int stringWidth(String s)
    {
        int width = 0;
        int index;

        if (!isCaseSensitive)
            s = s.toLowerCase();

        for (int i=0; i<s.length(); i++)
        {
            index = characterMap.indexOf(s.charAt(i));
            if (index != -1) width += characterWidths[index];
        }
        return width;
    }

    public int charsWidth(char[] chars, int offset, int length)
    {
        int width = 0;
        for (int i=offset; i<length; i++) width += charWidth(chars[i]);
        return width;
    }

    public void drawString(Graphics g, String s, int x, int y)
    {
        char ch;

        for (int i=0; i<s.length(); i++)
        {
            ch = s.charAt(i);
            drawChar(g, ch, x, y);
            x += charWidth(ch);
        }
    }

    public void drawChar(Graphics g, char ch, int x, int y)
    {
        if (!isCaseSensitive)
            ch = Character.toLowerCase(ch);

        int index = characterMap.indexOf(ch);
        if (index < 0)
        {
            index=characterMap.indexOf('?');
            if (index < 0) return;
        }

        Rectangle clipRect = g.getClipBounds();

        int offset_x = xPositions[index];

        g.clipRect(x, y, characterWidths[index], fontHeight);
        g.drawImage(fontImage, x - offset_x, y, null);
        g.setClip(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
    }  

    public static String[] splitString(String text, CustomFont font)
    {
        return splitString(text, font, Integer.MAX_VALUE, 0);
    }

    public BufferedImage getImage()
    {
        return fontImage;
    }

    public String getCharmap()
    {
        return characterMap;
    }

    /**
     * Breaks continuous text into set of small strings, each of them is not greater than the given limit.
     * @param text Text to break
     * @param font Font to use
     * @param windowWidth maximum string width.
     * @param numCols maximum string length in characters.
     * @return Formatted text
     *
     * The text is broken into set of lines depending on the given limits.
     * If you want to set a limit in pixels, use windowWidth, otherwise set this parameter to 0.
     * If you want to set a limit in characters, use numCols, otherwise set this parameter to 0.
     * If windowWidth and numCols are zero the text is broken by matching only new-line character.
     */
    public static String[] splitString(String text, CustomFont font, int windowWidth, int numCols)
    {
        Vector out = new Vector();
        int start = 0, end = 0, lastSpace = -1;
        int curWidth = 0;
        int curCols = 0;
        char ch;
        int length = text.length();

        for (int pos = 0; pos < length; pos++)
        {
            ch = text.charAt(pos);
            if (ch == '\n')
            {
                out.addElement(text.substring(start, pos));
                curWidth = 0;
                curCols = 0;
                lastSpace = start = pos + 1;
                continue;
            }
            if (ch == ' ' && lastSpace != pos - 1)
            {
                lastSpace = pos;
                if (start == pos)
                {
                    start++;
                    continue;
                }
            }

            if (windowWidth > 0)
                curWidth += font.charWidth(ch);
            else if (numCols > 0)
                curCols++;

            if ((windowWidth > 0 && curWidth > windowWidth) || (numCols > 0 && curCols > numCols))
            {
                end = (lastSpace > start) ? lastSpace + 1 : pos;
                out.addElement(text.substring(start, end));
                curWidth = 0;
                curCols = 0;
                lastSpace = start = end;
                pos = end - 1;
            }
        }


        if (start != length)
            out.addElement(text.substring(start, length));

        String[] outStrings = new String[out.size()];
        out.copyInto(outStrings);

        return outStrings;
    }


    public byte[] toByteArray()
    {
        byte[] data = null;
        DataOutputStream dos = null;
        ByteArrayOutputStream baos = null;

        try
        {
            baos = new ByteArrayOutputStream();
            dos = new DataOutputStream(baos);

            dos.writeBoolean(isCaseSensitive);
            dos.writeUTF(characterMap);
            dos.write(characterWidths);

            ImageIO.write(fontImage, "png", dos);

            dos.flush();
            data = baos.toByteArray();
        }
        catch (IOException ex)
        {

        }
        finally
        {
            if (dos != null)
                try { dos.close(); } catch (IOException ex) { /* ignored */ };
            if (baos != null)
                try { baos.close(); } catch (IOException ex) { /* ignored */ };

        }

        return data;
    }


}
